package com.cykj.common.utils;

import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * @BelongsProject: smart_recruit_backend
 * @BelongsPackage: com.cykj.common.utils
 * @Author: 小白糖糖
 * @CreateTime: 2025-08-29 16:12
 * @Description: TODO
 * @Version: 1.0
 */
public class FileCheckUtil {


    // 允许上传的文件后缀（根据业务扩展）
    private static final Set<String> ALLOWED_EXTENSIONS = new HashSet<>(Arrays.asList(
            "jpg", "jpeg", "png", "gif",  // 图片
            "pdf", "doc", "docx", "xls", "xlsx",  // 文档
            "mp4", "mp3"  // 音视频
    ));

    // 允许的MIME类型（与后缀对应）
    private static final Set<String> ALLOWED_MIME_TYPES = new HashSet<>(Arrays.asList(
            "image/jpeg", "image/png", "image/gif",
            "application/pdf",
            "application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
            "application/vnd.ms-excel", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            "video/mp4", "audio/mpeg"
    ));

    // 最大文件大小（5MB，根据业务调整）
    private static final long MAX_FILE_SIZE = 5 * 1024 * 1024;

    // 常见文件的Magic Number（文件头字节特征）
    private static final String JPG_HEADER = "FFD8FF";
    private static final String PNG_HEADER = "89504E47";
    private static final String GIF_HEADER = "47494638";
    private static final String PDF_HEADER = "25504446";

    /**
     * 校验文件合法性
     *
     * @param file 上传的文件
     * @throws IllegalArgumentException 校验失败时抛出异常
     */
    public static void validateFile(MultipartFile file) {
        // 1. 校验文件是否为空
        if (file.isEmpty()) {
            throw new IllegalArgumentException("上传文件不能为空");
        }

        // 2. 校验文件大小
        if (file.getSize() > MAX_FILE_SIZE) {
            throw new IllegalArgumentException("文件大小超过限制（最大" + MAX_FILE_SIZE / 1024 / 1024 + "MB）");
        }

        // 3. 校验文件名（防路径遍历）
        String originalFilename = file.getOriginalFilename();
        if (originalFilename == null || containsInvalidChars(originalFilename)) {
            throw new IllegalArgumentException("文件名包含非法字符");
        }

        // 4. 校验文件后缀
        String extension = getFileExtension(originalFilename);
        if (!ALLOWED_EXTENSIONS.contains(extension.toLowerCase())) {
            throw new IllegalArgumentException("不支持的文件类型：" + extension);
        }

        // 5. 校验MIME类型
        String contentType = file.getContentType();
        if (contentType == null || !ALLOWED_MIME_TYPES.contains(contentType)) {
            throw new IllegalArgumentException("文件MIME类型不合法：" + contentType);
        }

        // 6. 校验文件内容（通过文件头判断真实类型）
        try {
            validateFileHeader(file.getInputStream(), extension);
        } catch (IOException e) {
            throw new IllegalArgumentException("文件内容读取失败", e);
        }
    }

    /**
     * 校验文件名是否包含非法字符（防路径遍历）
     */
    private static boolean containsInvalidChars(String filename) {
        // 禁止包含路径分隔符或特殊字符
        return filename.contains("/") || filename.contains("\\")
                || filename.contains("..") || filename.matches(".*[<>?:*|\"].*");
    }

    /**
     * 获取文件后缀（不含点）
     */
    private static String getFileExtension(String filename) {
        int lastDotIndex = filename.lastIndexOf(".");
        if (lastDotIndex == -1 || lastDotIndex >= filename.length() - 1) {
            return ""; // 无后缀
        }
        return filename.substring(lastDotIndex + 1);
    }

    /**
     * 校验文件头（Magic Number），确保文件真实类型与后缀一致
     */
    private static void validateFileHeader(InputStream inputStream, String extension) throws IOException {
        // 读取文件前4字节（足够判断大多数文件类型）
        byte[] header = new byte[4];
        int read = inputStream.read(header);
        if (read < 4) {
            throw new IllegalArgumentException("文件内容不完整");
        }
        String headerHex = bytesToHex(header).toUpperCase();

        // 根据后缀校验对应的文件头
        switch (extension.toLowerCase()) {
            case "jpg":
            case "jpeg":
                if (!headerHex.startsWith(JPG_HEADER)) {
                    throw new IllegalArgumentException("文件内容与jpg类型不符");
                }
                break;
            case "png":
                if (!headerHex.equals(PNG_HEADER)) {
                    throw new IllegalArgumentException("文件内容与png类型不符");
                }
                break;
            case "gif":
                if (!headerHex.startsWith(GIF_HEADER)) {
                    throw new IllegalArgumentException("文件内容与gif类型不符");
                }
                break;
            case "pdf":
                if (!headerHex.startsWith(PDF_HEADER)) {
                    throw new IllegalArgumentException("文件内容与pdf类型不符");
                }
                break;
            // 其他类型可继续扩展
            default:
                // 非关键类型可跳过严格校验，或补充对应规则
        }
    }

    /**
     * 字节数组转十六进制字符串
     */
    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X", b));
        }
        return sb.toString();
    }
}


